All files / middleware security.js

0% Statements 0/34
0% Branches 0/24
0% Functions 0/8
0% Lines 0/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135                                                                                                                                                                                                                                                                             
/**
 * Security Middleware
 * Rate limiting, input sanitization, and security headers
 * 
 * @module middleware/security
 */
 
const rateLimit = require('express-rate-limit');
const validator = require('validator');
const { logger } = require('../config/logger');
 
/**
 * API Rate Limiter
 * General rate limiting for all API endpoints
 */
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 500, // Limit each IP to 500 requests per windowMs (increased from 100)
  message: 'Too many requests from this IP, please try again later.',
  standardHeaders: true,
  legacyHeaders: false,
  handler: (req, res) => {
    logger.warn(`Rate limit exceeded for IP: ${req.ip}`);
    res.status(429).json({
      success: false,
      error: 'Too many requests, please try again later.',
    });
  },
});
 
/**
 * Authentication Rate Limiter
 * Stricter rate limiting for authentication endpoints
 */
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // Limit each IP to 5 login requests per windowMs
  message: 'Too many login attempts, please try again later.',
  standardHeaders: true,
  legacyHeaders: false,
  skipSuccessfulRequests: true,
  handler: (req, res) => {
    logger.warn(`Auth rate limit exceeded for IP: ${req.ip}`);
    res.status(429).json({
      success: false,
      error: 'Too many login attempts, please try again in 15 minutes.',
    });
  },
});
 
/**
 * Input Sanitization Middleware
 * Sanitizes all user inputs to prevent XSS attacks
 */
function sanitizeInput(req, res, next) {
  try {
    // Sanitize body
    if (req.body && typeof req.body === 'object') {
      req.body = sanitizeObject(req.body);
    }
 
    // Sanitize query parameters
    if (req.query && typeof req.query === 'object') {
      req.query = sanitizeObject(req.query);
    }
 
    // Sanitize URL parameters
    if (req.params && typeof req.params === 'object') {
      req.params = sanitizeObject(req.params);
    }
 
    next();
  } catch (error) {
    logger.error('Error in input sanitization', { error: error.message });
    next(error);
  }
}
 
/**
 * Recursively sanitize object properties
 */
function sanitizeObject(obj) {
  const sanitized = {};
  
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      
      if (typeof value === 'string') {
        // Escape HTML and trim whitespace
        sanitized[key] = validator.escape(value.trim());
      } else if (typeof value === 'object' && value !== null) {
        // Recursively sanitize nested objects
        sanitized[key] = Array.isArray(value)
          ? value.map(item => typeof item === 'string' ? validator.escape(item.trim()) : item)
          : sanitizeObject(value);
      } else {
        sanitized[key] = value;
      }
    }
  }
  
  return sanitized;
}
 
/**
 * Validate email format
 */
function isValidEmail(email) {
  return validator.isEmail(email);
}
 
/**
 * Validate phone number format
 */
function isValidPhone(phone) {
  return validator.isMobilePhone(phone, 'any', { strictMode: false });
}
 
/**
 * Validate URL format
 */
function isValidURL(url) {
  return validator.isURL(url);
}
 
module.exports = {
  apiLimiter,
  authLimiter,
  sanitizeInput,
  isValidEmail,
  isValidPhone,
  isValidURL,
};